package simulations;


import forces.Force;
import forces.Gravity;
import forces.Viscosity;
import forces.WallRepulsion;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;


/**
 * Simulates objects moving around in a bounded environment.
 * 
 * @author Robert C. Duvall
 *         edited by Jenni Mercado
 */
public class Simulation {
    /**
     * List of the global forces acting in the simulation
     */
    private Gravity myGravity;
    private List<WallRepulsion> myWallRepulsion;
    private Viscosity myViscosity;
    private List<Entity> entities;
    /**
     * represents the Simulation's boundaries
     */
    private BoundingArea myBoundingArea;
    private double myCenterOfMassMagnitude;
    private double myCenterOfMassExpVal;
    private boolean centerOfMassEnabled;

    public Simulation (final BoundingArea boundingArea) {
        myGravity = new Gravity(90, 1); // default
        myWallRepulsion = new ArrayList<WallRepulsion>(); // default
        myViscosity = new Viscosity(.2); // default
        entities = new ArrayList<Entity>();

        this.myBoundingArea = boundingArea;
        // default to 0
        myCenterOfMassMagnitude = 0.0;
        myCenterOfMassExpVal = 1.0;
        centerOfMassEnabled = true;

    }

    /**
     * Add a Force to the simulation
     * 
     * @param forces
     */
    public void add (List<Force> forces) {

    }

    public void add (WallRepulsion wr) {
        myWallRepulsion.add(wr);
    }

    public void add (Gravity g) {
        myGravity = g;
    }

    public void add (Viscosity v) {
        myViscosity = v;
    }

    /**
     * paint all entities in our model
     * 
     * @param pen
     */
    public void paint (final Graphics2D pen) {
        myBoundingArea.paint(pen);
        for (Entity entity : entities) {
            entity.paint(pen);
        }
    }

    /**
     * Returns size (in pixels) of the Simulation area.
     * this now returns the BoundingArea Bounding Box
     */
    public Dimension getSize () {
        return myBoundingArea.getMyBounds();
    }

    public List<WallRepulsion> getWallRepulsions () {
        return myWallRepulsion;
    }

    public Gravity getGravity () {
        return myGravity;
    }

    public Viscosity getViscosity () {
        return myViscosity;
    }

    /**
     * update all entities in our simulation
     * 
     * @param elapsedTime
     */
    public void update (final double elapsedTime) {
        for (Entity entity : entities) {
            entity.update(this, elapsedTime);
        }
    }

    public void add (final Entity entity) {
        entities.add(entity);
    }

    /**
     * clears all Entities from this Simulation
     */
    public void clear () {
        entities.clear();
    }

    public List<Entity> getEntities () {
        return entities;
    }

    public void shrinkBounds () {
        myBoundingArea.shrink();
    }

    public void growBounds () {
        myBoundingArea.grow();
    }

    public void setCenterOfMassMagnitude (final double v) {
        myCenterOfMassMagnitude = v;
    }

    public void setCenterOfMassExpVal (final double v) {
        myCenterOfMassExpVal = v;
    }

    public double getMyCenterOfMassMagnitude () {
        return myCenterOfMassMagnitude;
    }

    public double getMyCenterOfMassExpVal () {
        return myCenterOfMassExpVal;
    }

    public boolean centerOfMassEnabled () {
        return centerOfMassEnabled;
    }

    public void toggleCenterOfMass () {
        if (centerOfMassEnabled) {
            centerOfMassEnabled = false;
        }
        else {
            centerOfMassEnabled = true;
        }
    }

    public Force getWall (final int i) {
        return myWallRepulsion.get(i - 1);
    }
}